#ifndef VEC_3_H_INCLUDED
#define VEC_3_H_INCLUDED

#include <math.h>

template <class T>
struct Vec3
{
	T X, Y, Z;

	Vec3() : X(0), Y(0), Z(0)
	{
		X = 0;
		Y = 0;
		Z = 0;
	}

	Vec3(T px, T py, T pz) : X(px), Y(py), Z(pz)
	{
		X = px;
		Y = py;
		Z = pz;
	}

	bool operator==(const Vec3& v)
	{
		if(X == v.X && Y == v.Y && Z == v.Z)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	Vec3& operator+=(const Vec3& v)
	{
		X += v.X;
		Y += v.Y;
		Z += v.Z;
		return *this;
	}

	Vec3& operator-=(const Vec3& v)
	{
		X -= v.X;
		Y -= v.Y;
		Z -= v.Z;
		return *this;
	}

	Vec3& operator*=(const Vec3& v)
	{
		X *= v.X;
		Y *= v.Y;
		Z *= v.Z;
		return *this;
	}

	Vec3& operator/=(const Vec3& v)
	{
		Vec3f result;
		result.X /= v.X;
		result.Y /= v.Y;
		result.Z /= v.Z;
		return result;
	}

	Vec3 operator+(const Vec3& v)
	{
		Vec3f result;
		result.X += v.X;
		result.Y += v.Y;
		result.Z += v.Z;
		return result;
	}

	/*Vec3 operator-(Vec3 v)const 
	{
		Vec3f result;
		result.X -= v.X;
		result.Y -= v.Y;
		result.Z -= v.Z;
		return result;
	}*/

	Vec3 operator-(const Vec3& v) const
	{
		Vec3f result;
		result.X -= v.X;
		result.Y -= v.Y;
		result.Z -= v.Z;
		return result;
	}

	Vec3 operator*(Vec3 v)
	{
		Vec3f result;
		result.X *= v.X;
		result.Y *= v.Y;
		result.Z *= v.Z;
		return result;
	}

	Vec3 operator/(const Vec3& v)
	{

		Vec3 result = *this;
		result /= v;
		return result;
	}
	
	Vec3 operator=(const Vec3& v)
	{
		X = v.X;
		Y = v.Y;
		Z = v.Z;
		return *this;
	}

	Vec3 operator*(float f) 
	{
		X *= f;
		Y *= f;
		Z *= f;
		return *this;
	}

	Vec3 operator/(float f) 
	{
		X /= f;
		Y /= f;
		Z /= f;
		return *this;
	}

	T LengthSqr()
	{
		T result = 0;
		result += X * X;
		result += Y * Y;
		result += Z * Z;
		return result;
	}

	T Length()
	{
		T result = 0;
		result += X * X;
		result += Y * Y;
		result += Z * Z;
		return Sqrtf(result);
	}

	void Normalise() 
	{
		T pre = 1 / (sqrt(LengthSqr()));
		X *= pre;
		Y *= pre;
		Z *= pre;
	}

	const Vec3 NormaliseReturn()
	{
		T pre = 1 / (sqrt(LengthSqr()));
		Vec3f result(0,0,0);
		result.X += X * pre;
		result.Y += Y * pre;
		result.Z += Z * pre;
		
		return result;
	}

	// Dot product is the cosine of the angle between two vectors
	// This is helpful because if the angle is 90 degrees, the dotproduct is 0 
	T DotProduct(const Vec3& v) const
	{
		T result = 0;
		result += (X * v.X);
		result += (Y * v.Y);
		result += (Z * v.Z);
		return result;
	}

	//Look up cross/dot again
	//Returns a normal
	Vec3 CrossProduct(const Vec3& v) const
	{
		Vec3 result;
		result.X = (Y * v.Z - Z * v.Y);
		result.Y = (Z * v.X - X * v.Z);
		result.Z = (X * v.Y - Y * v.X);
		return result;
	}


	float AngleBetween(const Vec3& v) const
	{
		Vec3 temp1 = v;
		Vec3 temp2 = this;
		temp1.Normalise();
		temp2.Normalise();

		return acos(temp1.DotProduct(temp2));
	}

};

typedef Vec3<int> Vec3i;
typedef Vec3<float> Vec3f;
// Normal = Normalise ( (n2 - origin) Cross product ( n1 - origin) )
static Vec3f GenerateNormal(Vec3f origin, Vec3f n2, Vec3f n1)
{
	Vec3f tempVec1 = n1 - origin;
	Vec3f tempVec2 = n2 - origin;
	Vec3f norm = tempVec2.CrossProduct(tempVec1);
	norm.Normalise();
	return norm;
}

#endif